/*
 * HSM Proxy Project.
 * Copyright (C) 2013 FedICT.
 * Copyright (C) 2013 Frank Cornelis.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.hsm.admin.webapp;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.ejb.EJB;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.primefaces.context.RequestContext;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;

import be.fedict.hsm.admin.webapp.security.RolesAllowed;
import be.fedict.hsm.entity.ApplicationEntity;
import be.fedict.hsm.entity.ApplicationKeyEntity;
import be.fedict.hsm.entity.CredentialEntity;
import be.fedict.hsm.entity.KeyStoreEntity;
import be.fedict.hsm.model.admin.ApplicationManagerBean;
import be.fedict.hsm.model.admin.KeyStoreManagerBean;
import be.fedict.hsm.model.exception.ExistingApplicationException;
import be.fedict.hsm.model.exception.ExistingApplicationKeyException;
import be.fedict.hsm.model.exception.ExistingCredentialException;
import be.fedict.hsm.model.security.AdministratorRoles;

@Named("hsmProxyApplication")
@ConversationScoped
@RolesAllowed(AdministratorRoles.ADMINISTRATOR)
public class ApplicationController implements Serializable {

	private static final long serialVersionUID = 1L;

	private static final Log LOG = LogFactory
			.getLog(ApplicationController.class);

	private String name;

	private String keyName;

	private ApplicationEntity selectedApplication;

	private ApplicationKeyEntity selectedApplicationKey;

	@EJB
	private ApplicationManagerBean applicationManagerBean;

	@EJB
	private KeyStoreManagerBean keyStoreManagerBean;

	@Inject
	private Conversation conversation;

	private String selectedApplicationKeyKeyStore;

	private String alias;

	private UploadedFile credentialFile;

	private CredentialEntity selectedCredential;

	public List<ApplicationEntity> getList() {
		return this.applicationManagerBean.getApplications();
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getKeyName() {
		return this.keyName;
	}

	public void setKeyName(String keyName) {
		this.keyName = keyName;
	}

	public String getAlias() {
		return this.alias;
	}

	public void setAlias(String alias) {
		this.alias = alias;
	}

	public void add(ActionEvent actionEvent) {
		try {
			this.applicationManagerBean.addApplication(this.name);
		} catch (ExistingApplicationException e) {
			FacesContext facesContext = FacesContext.getCurrentInstance();
			facesContext.addMessage("addForm:name", new FacesMessage(
					FacesMessage.SEVERITY_ERROR, "Existing application name.",
					null));
			return;
		}
		RequestContext requestContext = RequestContext.getCurrentInstance();
		requestContext.addCallbackParam("applicationAdded", true);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Added key store " + this.name,
				null));
		this.name = null;
	}

	public void addKey(ActionEvent actionEvent) {
		try {
			this.applicationManagerBean.addApplicationKey(this.keyName,
					this.selectedApplication);
		} catch (ExistingApplicationKeyException e) {
			FacesContext facesContext = FacesContext.getCurrentInstance();
			facesContext.addMessage("addForm:name", new FacesMessage(
					FacesMessage.SEVERITY_ERROR, "Existing application key.",
					null));
			return;
		}
		RequestContext requestContext = RequestContext.getCurrentInstance();
		requestContext.addCallbackParam("applicationKeyAdded", true);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Added application key "
						+ this.keyName, null));
		this.keyName = null;
	}

	public void selectApplication(ActionEvent actionEvent) {
		UIComponent component = actionEvent.getComponent();
		Map<String, Object> attributes = component.getAttributes();
		this.selectedApplication = (ApplicationEntity) attributes
				.get("selectedApplication");
		if (this.conversation.isTransient()) {
			this.conversation.begin();
		}
	}

	public void selectCredential(ActionEvent actionEvent) {
		UIComponent component = actionEvent.getComponent();
		Map<String, Object> attributes = component.getAttributes();
		this.selectedCredential = (CredentialEntity) attributes
				.get("selectedCredential");
		if (this.conversation.isTransient()) {
			this.conversation.begin();
		}
	}

	public void selectApplicationKey(ActionEvent actionEvent) {
		UIComponent uiComponent = actionEvent.getComponent();
		Map<String, Object> attributes = uiComponent.getAttributes();
		this.selectedApplicationKey = (ApplicationKeyEntity) attributes
				.get("selectedApplicationKey");
		KeyStoreEntity keyStoreEntity = this.selectedApplicationKey
				.getKeyStore();
		if (null != keyStoreEntity) {
			this.selectedApplicationKeyKeyStore = Long.toString(keyStoreEntity
					.getId());
		} else {
			this.selectedApplicationKeyKeyStore = null;
		}
		if (this.conversation.isTransient()) {
			this.conversation.begin();
		}
	}

	public ApplicationKeyEntity getSelectedApplicationKey() {
		return this.selectedApplicationKey;
	}

	public void removeSelectedApplication(ActionEvent actionEvent) {
		this.applicationManagerBean.removeApplication(this.selectedApplication);
		RequestContext requestContext = RequestContext.getCurrentInstance();
		requestContext.addCallbackParam("applicationRemoved", true);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Removed application "
						+ this.selectedApplication.getName(), null));
		this.selectedApplication = null;
		this.conversation.end();
	}

	public void removeSelectedCredential(ActionEvent actionEvent) {
		this.applicationManagerBean.removeCredential(this.selectedCredential);
		RequestContext requestContext = RequestContext.getCurrentInstance();
		requestContext.addCallbackParam("applicationCredential", true);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Removed credential "
						+ this.selectedCredential.getHash(), null));
		this.selectedCredential = null;
	}

	public void removeSelectedApplicationKey(ActionEvent actionEvent) {
		this.applicationManagerBean
				.removeApplicationKey(this.selectedApplicationKey);
		RequestContext requestContext = RequestContext.getCurrentInstance();
		requestContext.addCallbackParam("applicationKeyRemoved", true);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Removed application key "
						+ this.selectedApplicationKey.getName(), null));
		this.selectedApplicationKey = null;
	}

	public void saveSelectedApplicationKey(ActionEvent actionEvent) {
		this.applicationManagerBean.updateApplicationKey(
				this.selectedApplicationKey,
				this.selectedApplicationKeyKeyStore, this.alias);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Updated application key "
						+ this.selectedApplicationKey.getName(), null));
	}

	public String viewApplication(ApplicationEntity application) {
		this.selectedApplication = application;
		if (this.conversation.isTransient()) {
			this.conversation.begin();
		}
		return "/admin/application";
	}

	public ApplicationEntity getSelectedApplication() {
		return this.selectedApplication;
	}

	public String back() {
		this.conversation.end();
		return "/admin/applications";
	}

	public List<ApplicationKeyEntity> getKeys() {
		return this.applicationManagerBean.getKeys(this.selectedApplication);
	}

	public List<SelectItem> getKeyStores() {
		List<SelectItem> selectItems = new LinkedList<SelectItem>();
		selectItems.add(new SelectItem(null, "-none-"));
		List<KeyStoreEntity> keyStoreEntities = this.keyStoreManagerBean
				.getKeyStoreList();
		for (KeyStoreEntity keyStoreEntity : keyStoreEntities) {
			SelectItem selectItem = new SelectItem(keyStoreEntity.getId(),
					keyStoreEntity.getName());
			selectItems.add(selectItem);
		}
		return selectItems;
	}

	public String getSelectedApplicationKeyKeyStore() {
		return this.selectedApplicationKeyKeyStore;
	}

	public void setSelectedApplicationKeyKeyStore(
			String selectedApplicationKeyKeyStore) {
		this.selectedApplicationKeyKeyStore = selectedApplicationKeyKeyStore;
	}

	public void selectApplicationKeyKeyStore(ValueChangeEvent valueChangeEvent) {
		String keyStoreId = (String) valueChangeEvent.getNewValue();
		LOG.debug("select application key key store: " + keyStoreId);
		this.selectedApplicationKeyKeyStore = keyStoreId;
	}

	public List<SelectItem> getAliases() {
		List<SelectItem> selectItems = new LinkedList<SelectItem>();
		if (null == this.selectedApplicationKeyKeyStore) {
			return selectItems;
		}
		if (this.selectedApplicationKeyKeyStore.isEmpty()) {
			return selectItems;
		}
		List<String> aliases = this.keyStoreManagerBean.getKeyStoreAliases(Long
				.parseLong(this.selectedApplicationKeyKeyStore));
		for (String alias : aliases) {
			selectItems.add(new SelectItem(alias));
		}
		return selectItems;
	}

	public List<CredentialEntity> getCredentials() {
		return this.applicationManagerBean
				.getCredentials(this.selectedApplication);
	}

	public void handleCredentialUpload(FileUploadEvent fileUploadEvent) {
		LOG.debug("handleCredentialUpload");
		this.credentialFile = fileUploadEvent.getFile();
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage("addCredentialForm:credentialFileUpload",
				new FacesMessage(FacesMessage.SEVERITY_INFO,
						"Uploaded credential file.", null));
	}

	public void addCredential(ActionEvent actionEvent) {
		LOG.debug("addCredential");
		byte[] credential = this.credentialFile.getContents();
		LOG.debug("credential size: " + credential.length);
		try {
			this.applicationManagerBean.addApplicationCredential(
					this.selectedApplication, credential);
		} catch (ExistingCredentialException e) {
			FacesContext facesContext = FacesContext.getCurrentInstance();
			facesContext.addMessage("addCredentialForm:credentialFileUpload",
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Existing credential.", null));
			return;
		}
		RequestContext requestContext = RequestContext.getCurrentInstance();
		requestContext.addCallbackParam("applicationCredentialAdded", true);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.addMessage(null, new FacesMessage(
				FacesMessage.SEVERITY_INFO, "Added application credential.",
				null));
		this.credentialFile = null;
	}
}
